/*
 * Copyright (C) 2006 Christian Laireiter <liree@web.de>
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms 
 * of the GNU General Public License as published by the Free Software Foundation; either 
 * version 2 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License along with 
 *  this program; if not, write to the Free Software Foundation, 
 *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
 */
package mo.network.client.listeners;

import java.util.ArrayList;
import java.util.List;

import mo.network.server.data.Packet;


/**
 * Simple Eventqueue with threaded event handling.<br>
 * At each time, there will be a single thread running which will fire the
 * queued events one at a time.
 * 
 * @author Christian Laireiter
 */
public final class ReceiverEventQueue implements Runnable {
    /**
     * Logger for this class
     */

    private final List<IReceiver> listeners;

    private final List<Packet> queuedPackets;

    private boolean working = false;

    public ReceiverEventQueue() {
        this.queuedPackets = new ArrayList<Packet>();
        this.listeners = new ArrayList<IReceiver>();

    }

    public void addIReceiver(final IReceiver listener) {
        synchronized (this.listeners) {
            if (!this.listeners.contains(listener)) {
                this.listeners.add(listener);
            }
        }
    }

    private Packet dequeue() {
        Packet result = null;
        synchronized (this.queuedPackets) {
            if (!this.queuedPackets.isEmpty()) {
                result = this.queuedPackets.remove(0);
            }
        }
        return result;
    }

    public void enqueue(final Packet packet) {
        if (packet == null) {
            throw new NullPointerException();
        }
        synchronized (this.queuedPackets) {
            this.queuedPackets.add(packet);
            startWorker();
        }
    }

    public void removeIReceiver(final IReceiver listener) {
        synchronized (this.listeners) {
            this.listeners.remove(listener);
        }
    }

    public void run() {
        synchronized (this.queuedPackets) {
            if (this.working) {
                return;
            }
            this.working = true;
        }
        Packet current = null;
        while ((current = dequeue()) != null) {
            synchronized (this.listeners) {
                for (final IReceiver receiver : this.listeners) {
                    try {
                        receiver.packetReceived(current);
                    } catch (final Throwable t) {
                        t.printStackTrace();
                    }
                }
            }
        }
        synchronized (this.queuedPackets) {
            this.working = false;
            startWorker();
        }
    }

    private void startWorker() {
        synchronized (this.queuedPackets) {
            if (!this.working && !this.queuedPackets.isEmpty()) {
                new Thread(this, "ReceiverEventQueue").start();
            }
        }
    }

}
